package com.wingsoft.homeFlow.utils;

import cn.hutool.json.JSONUtil;

import com.wingsoft.common.constant.Constants;
import com.wingsoft.common.exception.ServiceException;
import com.wingsoft.common.utils.DateUtils;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.Resource;
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
import org.apache.velocity.tools.generic.*;
import org.ini4j.spi.EscapeTool;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VelocityUtils {

    public static void checkVarNameAvailable(Collection<String> keyList) {
        for (String key : keyList) {
            if (Arrays.asList(
                    "Alternator",
                    "ClassTool",
                    "CollectionTool",
                    "ComparisonDateTool",
                    "ContextTool",
                    "DateTool",
                    "EscapeTool",
                    "JsonTool",
                    "MathTool",
                    "NumberTool",
                    "RenderTool",
                    "ResourceTool",
                    "ValueParser",
                    "XmlTool",
                    "Integer",
                    "Long",
                    "Float",
                    "Double",
                    "Byte",
                    "Short",
                    "BigDecimal",
                    "DateUtils"
            ).contains(key)) {
                throw new ServiceException(key + "是内部工具，不可作为变量使用");
            }
            if (key.contains("\\.")) {
                throw new ServiceException(key + "变量中不支持.");
            }
        }
    }

    public static VelocityResult processTemplate(String template, Map<String, Object> context, boolean maskFlag) {
        return processTemplate(template, context, "", maskFlag);
    }

    public static VelocityResult processTemplate(String template, Map<String, Object> context, String logTag, boolean maskFlag) {
        if (StringUtils.isBlank(template)) {
            return new VelocityResult();
        }
        if (context == null) {
            context = new HashMap<>();
        }
        context.put("Alternator", new Alternator());
        context.put("ClassTool", new ClassTool());
        context.put("CollectionTool", new CollectionTool());
        context.put("ComparisonDateTool", new ComparisonDateTool());
        context.put("ContextTool", new ContextTool());
        context.put("DateTool", new DateTool());
        context.put("EscapeTool", new EscapeTool());
        context.put("JsonTool", new JsonTool());
        context.put("MathTool", new MathTool());
        context.put("NumberTool", new NumberTool());
        context.put("RenderTool", new RenderTool());
        context.put("ResourceTool", new ResourceTool());
        context.put("ValueParser", new ValueParser());
        context.put("XmlTool", new XmlTool());
        context.put("Integer", Integer.class);
        context.put("Long", Long.class);
        context.put("Float", Float.class);
        context.put("Double", Double.class);
        context.put("Byte", Byte.class);
        context.put("Short", Short.class);
        context.put("BigDecimal", BigDecimal.class);
        context.put("DateUtils", DateUtils.class);

        // 初始化引擎配置
        Properties props = new Properties();
        props.put(RuntimeConstants.INPUT_ENCODING, "UTF-8");
        props.put(RuntimeConstants.RESOURCE_LOADER, "string");
        props.put("string.resource.loader.class", StringResourceLoaderImpl.class.getName());
//        props.put("file.resource.loader.class", ClasspathResourceLoader.class.getName());

        // 创建Velocity引擎
        VelocityEngine velocityEngine = new VelocityEngine();
        velocityEngine.init(props);

        // 创建上下文并填充初始变量
        VelocityContext velocityContext = new VelocityContext(context);

        // 绑定事件到上下文
        EventCartridge eventCartridge = new EventCartridge();
        List<String> varCodeList = new ArrayList<>();
        List<Object> varValueList = new ArrayList<>();
        ReferenceInsertionEventHandler valueRecorderHandler = (ctx, varCode, varValue) -> {
            varCodeList.add(varCode);
            varValueList.add(varValue);
            if (maskFlag) {
                return "?";
            } else {
                return varValue;
            }
        };
        eventCartridge.addEventHandler(valueRecorderHandler);
        eventCartridge.attachToContext(velocityContext);

        // 执行模板渲染
        StringWriter stringWriter = new StringWriter();
        velocityEngine.evaluate(velocityContext, stringWriter, logTag, template);
        VelocityResult velocityResult = new VelocityResult();
        velocityResult.setTemplate(stringWriter.toString());
        velocityResult.setVarCodeList(varCodeList);
        velocityResult.setVarValueList(varValueList);
        return velocityResult;
    }

    public static List<String> getVarCodeList(String input) {
        if (StringUtils.isBlank(input)) {
            return new ArrayList<>();
        }
        String regex = "\\$\\{([^}]+)\\}"; // 正则表达式
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(input);
        List<String> results = new ArrayList<>();
        // 遍历所有匹配项
        while (matcher.find()) {
            results.add(matcher.group(1)); // 提取每个 ${...} 中的内容
        }
        return results;
    }

    public static String getMaskLogStr(VelocityResult velocityResult) {
        if (CollectionUtils.isNotEmpty(velocityResult.getVarCodeList())) {
            String str = velocityResult.getTemplate();
            for (int i = 0; i < velocityResult.getVarCodeList().size(); i++) {
                if (velocityResult.getVarCodeList().get(i).contains("${flowSource.password}")) {
                    str = str.replaceFirst("\\?", Constants.PASSWORD_MASK);
                } else {
                    str = str.replaceFirst("\\?", velocityResult.getVarValueList().get(i).toString());
                }
            }
            return str;
        }
        return velocityResult.getTemplate();
    }

    @Data
    public static class VelocityResult {
        private String template;
        private List<String> varCodeList;
        private List<Object> varValueList;
    }

    // 字符串资源加载器实现
    public static class StringResourceLoaderImpl extends StringResourceLoader {
        @Override
        public boolean isSourceModified(Resource resource) {
            return false;
        }

        @Override
        public long getLastModified(Resource resource) {
            return 0;
        }
    }

    public static void main(String[] args) {
        // 示例模板
        String template = "#set($user = 'John')\n"
                + "Welcome ${user},\n"
                + "#if($age > 18)\n"
                + "  Your status: $!{status1}\n"
                + "#end\n"
                + "#if($age < 18)\n"
                + "  Your status: $!{status2}\n"
                + "#end\n"
                + "Current score: $score\n"
                + "[#foreach ($i in $list) " +
                " ${i}#if(!$foreach.last), #end" +
                "#end]\n" +
                "$DATE.format('yyyy-MM-dd',${date})" +
                "\n\n" +
                "$STR.join($list, \", \")" +
                "【#if(${ids}) 集合不为空 #else 集合为空 #end】";

        template = "#set($s1_code1 = $Integer.parseInt($s1_code)) \r\n " +
                "hospital/home_test(P_IN:IN::${s1_code1}, P_OUT:INOUT:12:${out1}, out_return:OUT:-10:${out2})";

        template = "#set($str = \"123\")\n" +
                "#set($strLike = $Integer.parseInt($str))\n" +
                "$strLike" +
                "${Integer.parseInt($str)}" +
                "$Short.parseShort($str)" +
                "$DateUtils.stringToDate($dateStr,'yyyy-MM-dd')";
        template =
                "#set($str = '')" +
                        "#foreach($item in ${t1_list})" +
                        "#set($str = $str + ${item.dept} + '-' + ${item.inp} + ';')" +
                        "#end \n" +
                        "最后拼接的数据：${str}";
        template = "$DateTool.format('yyyy-MM-dd HH:mm:ss',$DateTool.getSystemDate())" +
                "$DateTool.getSystemDate()";
        // 上下文变量
        Map<String, Object> context = new HashMap<>();
        context.put("t1_list", JSONUtil.parseArray("\t\n" +
                "[{\"dept\":\"d1\",\"inp\":\"u11\"},{\"dept\":\"d1\",\"inp\":\"u12\"},{\"dept\":\"d2\",\"inp\":\"u2\"}]"));
        context.put("age", 10);
        context.put("score", 95);
        context.put("status1", "成年");
        context.put("status2", "未成年");
        context.put("list", new ArrayList<String>() {{
            add("1");
            add("2");
            add("3");
        }});
        context.put("date", new Date());
        context.put("sys_date_year", new Date());
        context.put("ids", Collections.emptyList());
        context.put("s1_code", "11");
        context.put("value2", 22);
        context.put("dateStr", "2021-01-01");

//        变量中不支持.
//        templateContentVars.put("sys.date_year", new Date());
        System.out.println("===============================================");
        VelocityResult velocityResult = VelocityUtils.processTemplate(template, context, false);
        System.out.println("===============================================");
        System.out.println(velocityResult.getTemplate());
        for (Object object : velocityResult.getVarValueList()) {
            if (Objects.isNull(object)) {
                velocityResult.setTemplate(velocityResult.getTemplate().replaceFirst("\\?", "null"));
            } else {
                velocityResult.setTemplate(velocityResult.getTemplate().replaceFirst("\\?", object.toString()));
            }
        }
        System.out.println("===============================================");
        System.out.println(velocityResult);

    }

}
